﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Reflection;
using System.Text;

namespace SAP.BusinessOne.WebBridge.Framework
{
    class ObjectCache
    {
        internal IDictionary<string, object> _instances;
        internal IDictionary<string, Tuple<Type, Type>> _types;
        internal IDictionary<string, Tuple<MethodInfo, TypeCode[], TypeCode>> _params;

        public ObjectCache(Assembly target)
        {
            InitCache(target);
        }

        public int CreateObject(string type)
        {
            if (!_types.ContainsKey(type))
                throw new ArgumentException("Unknown target type");

            var obj = Activator.CreateInstance(_types[type].Item1);
            var hash = obj.GetHashCode();

            var key = GetKey(type, hash);
            _instances.Add(key, obj);

            return hash;
        }

        public object GetObject(string type, int hash)
        {
            var key = GetKey(type, hash);
            return _instances[key];
        }

        public Tuple<MethodInfo, TypeCode[], TypeCode> GetMethod(string type, string method)
        {
            return _params[GetKey(type, method)];
        }

        private void InitCache(Assembly target)
        {
            _instances = new Dictionary<string, object>();
            _types = new Dictionary<string, Tuple<Type, Type>>();
            _params = new Dictionary<string, Tuple<MethodInfo, TypeCode[], TypeCode>>();

            // types
            var typeList = target.GetTypes().Where(p => p.GetCustomAttributes(typeof(ExportAttribute), false).Any());
            foreach (var type in typeList)
            {
                var export = type.GetCustomAttributes(typeof(ExportAttribute), false).First() as ExportAttribute;
                var contract = export.ContractType;
                _types.Add(type.FullName, Tuple.Create(type, contract));
            }

            // methods
            foreach (var pair in _types)
            {
                var name = pair.Key;
                var type = pair.Value.Item2;
                var methods = type.GetMethods();

                foreach (var method in methods)
                {
                    var key = GetKey(name, method.Name);
                    var ip = method.GetParameters().Select(p => Type.GetTypeCode(p.ParameterType)).ToArray();
                    var op = Type.GetTypeCode(method.ReturnParameter.ParameterType);

                    _params.Add(key, Tuple.Create(method, ip, op));
                }
            }
        }

        private string GetKey(string type, int hash)
        {
            var buffer = new StringBuilder();
            buffer.Append(type);
            buffer.Append(hash);
            return buffer.ToString();
        }

        private string GetKey(string type, string method)
        {
            var buffer = new StringBuilder();
            buffer.Append(type);
            buffer.Append(method);
            return buffer.ToString();
        }

    }
}
